//**********************************************************************
//	Programmer	-	Sujal Bista
//	File Name	-	picture.cpp
//	Description	:	
//					general picture handler (some parts for HDR fromat
//					is from NVIDIA's HDR example)
//
//**********************************************************************

#include "picture.h"
#include "global.h"

#include <fstream>
using namespace std;

//default constructor
picture::picture(void):data(NULL)
{
}; 

//constructor
picture::picture(
	const unsigned int &width,
	const unsigned int &height,
	unsigned int bytesPerPixel,
	IMAGE_FORMAT internalFormat,
	IMAGE_FORMAT format,
	float gamma,
	float exposure
	)
{
	data=NULL;
	//allocate memory for picture
	allocate(width,height,bytesPerPixel,internalFormat,format,gamma,exposure);
}

//constructor
picture::picture(const picture &pic)
{
	data=NULL;

	//allocate memory for picture
	allocate(pic.width,pic.height,pic.bytesPerPixel,pic.internalFormat,pic.format,pic.gamma,pic.exposure);

	//copy the memory
	memcpy(data,pic.data,getLineSize()*height);
}

//allocates memory for picture
void picture::allocate(
	const unsigned int &width,
	const unsigned int &height,
	unsigned int bytesPerPixel,
	IMAGE_FORMAT internalFormat,
	IMAGE_FORMAT format,
	float gamma,
	float exposure
	)
{
	//frees memory if allocated before
	clear();
	this->width=width;
	this->height=height;
	this->bytesPerPixel=bytesPerPixel;
	this->internalFormat=internalFormat;
	this->format=format;
	this->gamma=gamma;
	this->exposure=exposure;
	unsigned int lineSize=getLineSize();
	data=new unsigned char[height*lineSize];
	memset(data,0,height*lineSize);
}

//returns size of line in bytes
//padded with 32 bit align
unsigned int picture::getLineSize(void) const
{
	int paddedWidth=width*bytesPerPixel;
	paddedWidth+=(4-paddedWidth%4)%4;
	return ((unsigned int)paddedWidth);
}

//flips an image horizontally
void picture::flipHorizontal(void)
{
	unsigned int lineSize=getLineSize();
	unsigned int halfWidth=width/2;

	for(unsigned int y=0;y<height;y++)
	{
		for(unsigned int x=0;x<halfWidth;x++)
		{
			//compute two positions to switch
			unsigned int posA=y*lineSize+x*bytesPerPixel;
			unsigned int posB=y*lineSize+(width-(x+1))*bytesPerPixel;

			unsigned char temp;
			//switch the variables
			for(unsigned int z=0;z<bytesPerPixel;z++)
				SWAP(data[posA+z],data[posB+z],temp);
		}
	}
}

//flips an image vertically
void picture::flipVertically(void)
{
	unsigned int lineSize=getLineSize();
	unsigned int halfHeight=height/2;

	for(unsigned int y=0;y<halfHeight;y++)
	{
		for(unsigned int x=0;x<width;x++)
		{
			//compute two positions to switch
			unsigned int posA=y*lineSize+x*bytesPerPixel;
			unsigned int posB=(height-(y+1))*lineSize+x*bytesPerPixel;

			unsigned char temp;
			//switch the variables
			for(unsigned int z=0;z<bytesPerPixel;z++)
				SWAP(data[posA+z],data[posB+z],temp);
		}
	}
}

//flips an image horizontally and vertically at the same time
void picture::flipHorizontalAndVertically(void)
{
	unsigned int lineSize=getLineSize();
	unsigned int halfHeight=height/2;

	for(unsigned int y=0;y<halfHeight;y++)
	{
		for(unsigned int x=0;x<width;x++)
		{
			//compute two positions to switch
			unsigned int posA=y*lineSize+x*bytesPerPixel;
			unsigned int posB=(height-(y+1))*lineSize+(width-(x+1))*bytesPerPixel;

			unsigned char temp;
			//switch the variables
			for(unsigned int z=0;z<bytesPerPixel;z++)
				SWAP(data[posA+z],data[posB+z],temp);
		}
	}
}
//inverts the color
void picture::invertColor(void)
{
	unsigned int lineSize=getLineSize();
	for(unsigned int y=0;y<height;y++)
	{
		for(unsigned int x=0;x<width;x++)
		{
			//position in the array
			unsigned int pos=y*lineSize+x*bytesPerPixel;
			
			for(unsigned int z=0;z<bytesPerPixel;z++)
				data[pos+z]=(255-data[pos+z]);
		}
	}
}


//inverts the color
void picture::BGR2RGB(void)
{
	unsigned int lineSize=getLineSize();
	unsigned char temp;

	for(unsigned int y=0;y<height;y++)
	{
		for(unsigned int x=0;x<width;x++)
		{
			//position in the array
			unsigned int pos=y*lineSize+x*bytesPerPixel;
					
			//swap the data
			SWAP(data[pos],data[pos+2],temp);
		}
	}
}

//takes maximum of two images
void picture::bigger(const picture &pData)
{
	unsigned int lineSize1=getLineSize();
	unsigned int lineSize2=pData.getLineSize();

	unsigned int minHeight=MIN(height,pData.height);
	unsigned int minWidth=MIN(width,pData.width);
	unsigned int minBytesPerPixel=MIN(bytesPerPixel,pData.bytesPerPixel);

	for(unsigned int y=0;y<minHeight;y++)
	{
		for(unsigned int x=0;x<minWidth;x++)
		{
			//position in the array
			unsigned int pos1=y*lineSize1+x*bytesPerPixel;
			unsigned int pos2=y*lineSize2+x*pData.bytesPerPixel;
			
			for(unsigned int z=0;z<minBytesPerPixel;z++)
				data[pos1+z]=MAX(data[pos1+z],pData.data[pos2+z]);
		}
	}
}

//add the image values
void picture::add(const picture &pData)
{
	unsigned int lineSize1=getLineSize();
	unsigned int lineSize2=pData.getLineSize();

	unsigned int minHeight=MIN(height,pData.height);
	unsigned int minWidth=MIN(width,pData.width);
	unsigned int minBytesPerPixel=MIN(bytesPerPixel,pData.bytesPerPixel);

	for(unsigned int y=0;y<minHeight;y++)
	{
		for(unsigned int x=0;x<minWidth;x++)
		{
			//position in the array
			unsigned int pos1=y*lineSize1+x*bytesPerPixel;
			unsigned int pos2=y*lineSize2+x*pData.bytesPerPixel;
			
			for(unsigned int z=0;z<minBytesPerPixel;z++)
				data[pos1+z]=(char)(CLAMP((int)(data[pos1+z]+pData.data[pos2+z]),0,255));
		}
	}
}

//substract the image values
void picture::sub(const picture &pData)
{
	unsigned int lineSize1=getLineSize();
	unsigned int lineSize2=pData.getLineSize();

	unsigned int minHeight=MIN(height,pData.height);
	unsigned int minWidth=MIN(width,pData.width);
	unsigned int minBytesPerPixel=MIN(bytesPerPixel,pData.bytesPerPixel);

	for(unsigned int y=0;y<minHeight;y++)
	{
		for(unsigned int x=0;x<minWidth;x++)
		{
			//position in the array
			unsigned int pos1=y*lineSize1+x*bytesPerPixel;
			unsigned int pos2=y*lineSize2+x*pData.bytesPerPixel;
			
			for(unsigned int z=0;z<minBytesPerPixel;z++)
				data[pos1+z]=(char)(CLAMP((int)(data[pos1+z]-pData.data[pos2+z]),0,255));
		}
	}
}

//max of two images
void picture::maximum(const picture &pData)
{
	unsigned int lineSize1=getLineSize();
	unsigned int lineSize2=pData.getLineSize();

	unsigned int minHeight=MIN(height,pData.height);
	unsigned int minWidth=MIN(width,pData.width);
	unsigned int minBytesPerPixel=MIN(bytesPerPixel,pData.bytesPerPixel);

	for(unsigned int y=0;y<minHeight;y++)
	{
		for(unsigned int x=0;x<minWidth;x++)
		{
			//position in the array
			unsigned int pos1=y*lineSize1+x*bytesPerPixel;
			unsigned int pos2=y*lineSize2+x*pData.bytesPerPixel;
			
			for(unsigned int z=0;z<minBytesPerPixel;z++)
				data[pos1+z]=MAX(data[pos1+z],pData.data[pos2+z]);
		}
	}
}

//min of two images
void picture::minimum(const picture &pData)
{
	unsigned int lineSize1=getLineSize();
	unsigned int lineSize2=pData.getLineSize();

	unsigned int minHeight=MIN(height,pData.height);
	unsigned int minWidth=MIN(width,pData.width);
	unsigned int minBytesPerPixel=MIN(bytesPerPixel,pData.bytesPerPixel);

	for(unsigned int y=0;y<minHeight;y++)
	{
		for(unsigned int x=0;x<minWidth;x++)
		{
			//position in the array
			unsigned int pos1=y*lineSize1+x*bytesPerPixel;
			unsigned int pos2=y*lineSize2+x*pData.bytesPerPixel;
			
			for(unsigned int z=0;z<minBytesPerPixel;z++)
				data[pos1+z]=MIN(data[pos1+z],pData.data[pos2+z]);
		}
	}
}

//replace one image content with another
void picture::replace(const picture &pData,const unsigned int &startX,const unsigned int &startY)
{
	//replace a part of an image
	mix(pData,((const float)startX),((const float)startY));
}

//mixes two images
void picture::mix(const picture &pData,const float &startX,const float &startY,const float &multX,const float &multY)
{
	unsigned int lineSize1=getLineSize();
	unsigned int lineSize2=pData.getLineSize();

	unsigned int minBytesPerPixel=MIN(bytesPerPixel,pData.bytesPerPixel);

	for(unsigned int y=0;y<pData.height;y++)
	{
		for(unsigned int x=0;x<pData.width;x++)
		{
			unsigned int outPosX=(unsigned int)(x*multX+startX+.5f);
			unsigned int outPosY=(unsigned int)(y*multY+startY+.5f);

			if(outPosX>=width || outPosY>=height)
				continue;

			//position in the array
			unsigned int pos1=outPosY*lineSize1+outPosX*bytesPerPixel;
			unsigned int pos2=y*lineSize2+x*pData.bytesPerPixel;
			
			for(unsigned int z=0;z<minBytesPerPixel;z++)
				data[pos1+z]=pData.data[pos2+z];
		}
	}
}

//mixes two images based on the position
void picture::mixPlus(const picture &pData,unsigned int *horizontalPos, unsigned int *verticalPos,const unsigned int &horizontalPosCount, const unsigned int &verticalPosCount,const unsigned int &startX,const unsigned int &startY)
{
	unsigned int lineSize1=getLineSize();
	unsigned int lineSize2=pData.getLineSize();

	unsigned int minBytesPerPixel=MIN(bytesPerPixel,pData.bytesPerPixel);

	unsigned int minWidth=MIN(horizontalPosCount,pData.width);
	unsigned int minHeight=MIN(verticalPosCount,pData.height);

	for(unsigned int y=0;y<minHeight;y++)
	{
		for(unsigned int x=0;x<minWidth;x++)
		{
			unsigned int outPosX=horizontalPos[x]+startX;
			unsigned int outPosY=verticalPos[y]+startY;

			if(outPosX>=width || outPosY>=height)
				continue;

			//position in the array
			unsigned int pos1=outPosY*lineSize1+outPosX*bytesPerPixel;
			unsigned int pos2=y*lineSize2+x*pData.bytesPerPixel;
			
			for(unsigned int z=0;z<minBytesPerPixel;z++)
				data[pos1+z]=pData.data[pos2+z];
		}
	}
}

//adds padding on the image
void picture::addBoundary(unsigned int padSize)
{
			
	unsigned int tempWidth=width;
	unsigned int tempHeight=height;
	unsigned char *tempData=data;
	unsigned int srcPaddedWidth=width*bytesPerPixel;
	srcPaddedWidth+=(4-(signed)srcPaddedWidth%4)%4;

	//increase the height
	height+=2*padSize;
	width+=2*padSize;
	unsigned int destPaddedWidth=width*bytesPerPixel;
	destPaddedWidth+=(4-(signed)destPaddedWidth%4)%4;
	//allocate new memory
	data=new unsigned char[destPaddedWidth*height];

	int startLocationDest, startLocationSrc;

	//copy the actual chunk;
	startLocationDest=padSize*destPaddedWidth+padSize*bytesPerPixel;
	startLocationSrc=0;
	for(unsigned int j=0;j<tempHeight;j++)
	{
		for(unsigned int i=0;i<tempWidth;i++)
		{ 
			int indexDest=j*destPaddedWidth+i*bytesPerPixel;
			int indexSrc=j*srcPaddedWidth+i*bytesPerPixel;
			for(unsigned int h=0;h<bytesPerPixel;h++)
				data[startLocationDest+indexDest+h]=tempData[startLocationSrc+indexSrc+h];
		}
	}

	///////////////////////////////////////////////////////////////////
	//clamp the added region with boundary pixels

	//make top boundary from itself
	startLocationDest=padSize*bytesPerPixel;
	startLocationSrc=padSize*destPaddedWidth+padSize*bytesPerPixel;
	for(unsigned int j=0;j<padSize;j++)
	{
		for(unsigned int i=0;i<tempWidth;i++)
		{ 
			int indexDest=j*destPaddedWidth+i*bytesPerPixel;
			int indexSrc=((padSize-1)-j)*destPaddedWidth+i*bytesPerPixel;
			for(unsigned int h=0;h<bytesPerPixel;h++)
				data[startLocationDest+indexDest+h]=data[startLocationSrc+indexSrc+h];
		}
	}

	//make bottom boundary from itself
	startLocationDest=(tempHeight+padSize)*destPaddedWidth+padSize*bytesPerPixel;
	startLocationSrc=((tempHeight-1)+padSize)*destPaddedWidth+padSize*bytesPerPixel;
	for(unsigned int j=0;j<padSize;j++)
	{
		for(unsigned int i=0;i<tempWidth;i++)
		{ 
			unsigned int indexDest=j*destPaddedWidth+i*bytesPerPixel;
			int indexSrc=-(signed)(j*destPaddedWidth)+i*bytesPerPixel;
			for(unsigned int h=0;h<bytesPerPixel;h++)
				data[startLocationDest+indexDest+h]=data[startLocationSrc+indexSrc+h];
		}
	}

	//make left boundary from itself
	startLocationDest=0;
	startLocationSrc=padSize*bytesPerPixel;
	for(unsigned int j=0;j<height;j++)
	{
		for(unsigned int i=0;i<padSize;i++)
		{ 
			unsigned int indexDest=j*destPaddedWidth+i*bytesPerPixel;
			int indexSrc=j*destPaddedWidth+((padSize-1)-i)*bytesPerPixel;
			for(unsigned int h=0;h<bytesPerPixel;h++)
				data[startLocationDest+indexDest+h]=data[startLocationSrc+indexSrc+h];
		}
	}

	//make right boundary from itself
	startLocationDest=(padSize+tempWidth)*bytesPerPixel;
	startLocationSrc=(padSize+(tempWidth-1))*bytesPerPixel;
	for(unsigned int j=0;j<height;j++)
	{
		for(unsigned int i=0;i<padSize;i++)
		{ 
			unsigned int indexDest=j*destPaddedWidth+i*bytesPerPixel;
			int indexSrc=-((signed)i)*bytesPerPixel+j*destPaddedWidth;
			for(unsigned int h=0;h<bytesPerPixel;h++)
				data[startLocationDest+indexDest+h]=data[startLocationSrc+indexSrc+h];
		}
	}
	///////////////////////////////////////////////////////////////////
			
	delete[] tempData;
}

//removes boundary from the image
void picture::removeBoundary(unsigned int padSize)
{
	unsigned char *tempData=data;
	unsigned int srcPaddedWidth=width*bytesPerPixel;
	srcPaddedWidth+=(4-(signed)srcPaddedWidth%4)%4;

	//increase the height
	height-=2*padSize;
	width-=2*padSize;
	unsigned int destPaddedWidth=width*bytesPerPixel;
	destPaddedWidth+=(4-(signed)destPaddedWidth%4)%4;
	//allocate new memory
	data=new unsigned char[destPaddedWidth*height];

	unsigned int startLocationDest, startLocationSrc;

	//copy the actual chunk;
	startLocationDest=0;
	startLocationSrc=padSize*srcPaddedWidth+padSize*bytesPerPixel;

	for(unsigned int j=0;j<height;j++)
	{
		for(unsigned int i=0;i<width;i++)
		{ 
			unsigned int indexDest=j*destPaddedWidth+i*bytesPerPixel;
			unsigned int indexSrc=j*srcPaddedWidth+i*bytesPerPixel;
			for(unsigned int h=0;h<bytesPerPixel;h++)
				data[startLocationDest+indexDest+h]=tempData[startLocationSrc+indexSrc+h];
		}
	}
			
	delete[] tempData;
}


//load the bitmap form a file
bool picture::readBMP(const char *fileName)
{
	BITMAPFILEHEADER	bitmapFileHeader;		// bitmap file header
	BITMAPINFOHEADER	bitmapInfoHeader;		// bitmap info header
	ifstream in(fileName,ios::binary);
	if (!in)
		return false;

	// read the bitmap file header
	//in.read((char *)&bitmapFileHeader, sizeof(BITMAPFILEHEADER));
	in.read((char *)&bitmapFileHeader.bfType, sizeof(unsigned short));
	in.read((char *)&bitmapFileHeader.bfSize, sizeof(unsigned));
	in.read((char *)&bitmapFileHeader.bfReserved1, sizeof(unsigned short));
	in.read((char *)&bitmapFileHeader.bfReserved2, sizeof(unsigned short));
	in.read((char *)&bitmapFileHeader.bfOffBits, sizeof(unsigned));
	
	// verify that this is a bitmap by checking for the universal bitmap id
	if (bitmapFileHeader.bfType != 0x4D42)
	{
		in.close();
		return false;
	}

	// read the bitmap information header
	//in.read((char *)&bitmapInfoHeader, sizeof(BITMAPINFOHEADER));
	in.read((char *)&bitmapInfoHeader.biSize, sizeof(unsigned));
	in.read((char *)&bitmapInfoHeader.biWidth, sizeof(int));
	in.read((char *)&bitmapInfoHeader.biHeight, sizeof(int));
	in.read((char *)&bitmapInfoHeader.biPlanes, sizeof(unsigned short));
	in.read((char *)&bitmapInfoHeader.biBitCount, sizeof(unsigned short));
	in.read((char *)&bitmapInfoHeader.biCompression, sizeof(unsigned));
	in.read((char *)&bitmapInfoHeader.biSizeImage, sizeof(unsigned));
	in.read((char *)&bitmapInfoHeader.biXPelsPerMeter, sizeof(int));
	in.read((char *)&bitmapInfoHeader.biYPelsPerMeter, sizeof(int));
	in.read((char *)&bitmapInfoHeader.biClrUsed, sizeof(unsigned int));
	in.read((char *)&bitmapInfoHeader.biClrImportant, sizeof(unsigned int));
	if(in.bad())
	{
		in.close();
		return false;
	}

	// move file pointer to beginning of bitmap data
	in.seekg(bitmapFileHeader.bfOffBits);

	if(bitmapInfoHeader.biSizeImage==0)
	{
		int scrPaddedWidth=bitmapInfoHeader.biWidth*bitmapInfoHeader.biBitCount/8;
		scrPaddedWidth+=(4-scrPaddedWidth%4)%4;
		bitmapInfoHeader.biSizeImage=scrPaddedWidth*bitmapInfoHeader.biHeight;
	}
	
	height=bitmapInfoHeader.biHeight;
	width=bitmapInfoHeader.biWidth;

	switch(bitmapInfoHeader.biBitCount)
	{
		case 24:
			bytesPerPixel=3;	//24 bit picture
			internalFormat=IMAGE_FORMAT_BC1;
			format=IMAGE_FORMAT_BGR;
			break;
		case 32:
			bytesPerPixel=4;	//32 bit picture
			internalFormat=IMAGE_FORMAT_BC3;
			format=IMAGE_FORMAT_BGRA;
			break;
		default:
			in.close();
			return false;
	}
	gamma=1.0f;
	exposure=1.0f;	
	// allocate enough memory for the bitmap image data
	data = new unsigned char[bitmapInfoHeader.biSizeImage];

	// verify memory allocation
	if (!data)
	{
		in.close();
		return false;
	}

	// read in the bitmap image data
	in.read((char *)data, bitmapInfoHeader.biSizeImage);

	// make sure bitmap image data was read
	if(in.bad())
	{
		in.close();
		return false;
	}
	
	// close the file and return the bitmap image data
	in.close();
	return true;
}

//writes bitmap data to the file 
bool picture::writeBMP(const char *fileName, bool padded)
{
	BITMAPFILEHEADER bitmapFileHeader;	// bitmap file header
	BITMAPINFOHEADER bitmapInfoHeader;	// bitmap info header
	ofstream out(fileName,ios::binary);
	if (!out)
		return false;

	// define the bitmap file header
	bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
	bitmapFileHeader.bfType = 0x4D42;
	bitmapFileHeader.bfReserved1 = 0;
	bitmapFileHeader.bfReserved2 = 0;
	//bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	bitmapFileHeader.bfOffBits = 54;
	
	// define the bitmap information header
	bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmapInfoHeader.biPlanes = 1;
	bitmapInfoHeader.biBitCount = (WORD)(bytesPerPixel*8);		// bits
	bitmapInfoHeader.biCompression = BI_RGB;				// no compression
	bitmapInfoHeader.biSizeImage = width * height * bytesPerPixel;	// width * height * (RGB bytes)
	bitmapInfoHeader.biXPelsPerMeter = 0;
	bitmapInfoHeader.biYPelsPerMeter = 0;
	bitmapInfoHeader.biClrUsed = 0;
	bitmapInfoHeader.biClrImportant = 0;
	bitmapInfoHeader.biWidth = width;					// bitmap width
	bitmapInfoHeader.biHeight = height;					// bitmap height

	int destPaddedWidth=width*bytesPerPixel;
	destPaddedWidth+=(4-destPaddedWidth%4)%4;
	int scrPaddedWidth=destPaddedWidth;
	//check if the data is already padded
	if(!padded)
		scrPaddedWidth=width*bytesPerPixel;

	bitmapInfoHeader.biSizeImage = destPaddedWidth * height;
	unsigned char *tempData=new unsigned char[destPaddedWidth*height];
	int x,y;

	switch(bitmapInfoHeader.biBitCount)
	{
		case 8:
				// just copy the image with horizontal alignment
				for(y=(signed)height-1;y>=0;--y)
				{
					for(x=0;x<(signed)(width);++x)
					{
						tempData[y*destPaddedWidth+x]=data[y*scrPaddedWidth+x];
					}
				}
			break;

		case 24:

			if(format==IMAGE_FORMAT_BGR)
			{
				// switch the image data from BGR to BGR
				for(y=(signed)height-1;y>=0;y--)
				{					
					for(x=0;x<(signed)(width);x++)
					{
						tempData[y*destPaddedWidth+x*bytesPerPixel]=data[y*scrPaddedWidth+x*bytesPerPixel];
						tempData[y*destPaddedWidth+x*bytesPerPixel+1]=data[y*scrPaddedWidth+x*bytesPerPixel+1];
						tempData[y*destPaddedWidth+x*bytesPerPixel+2]=data[y*scrPaddedWidth+x*bytesPerPixel+2];
					}
				}
			}
			else
			{
				// switch the image data from RGB to BGR
				for(y=(signed)height-1;y>=0;y--)
				{					
					for(x=0;x<(signed)(width);x++)
					{
						tempData[y*destPaddedWidth+x*bytesPerPixel]=data[y*scrPaddedWidth+x*bytesPerPixel+2];
						tempData[y*destPaddedWidth+x*bytesPerPixel+1]=data[y*scrPaddedWidth+x*bytesPerPixel+1];
						tempData[y*destPaddedWidth+x*bytesPerPixel+2]=data[y*scrPaddedWidth+x*bytesPerPixel];
					}
				}
			}
			break;
		case 32:
			if(format==IMAGE_FORMAT_BGRA)
			{
				// switch the image data from BGR to BGR
				for(y=(signed)height-1;y>=0;y--)
				{
					for(x=0;x<(signed)(width);x++)
					{
						tempData[y*destPaddedWidth+x*bytesPerPixel]=data[y*scrPaddedWidth+x*bytesPerPixel];
						tempData[y*destPaddedWidth+x*bytesPerPixel+1]=data[y*scrPaddedWidth+x*bytesPerPixel+1];
						tempData[y*destPaddedWidth+x*bytesPerPixel+2]=data[y*scrPaddedWidth+x*bytesPerPixel+2];
						tempData[y*destPaddedWidth+x*bytesPerPixel+3]=data[y*scrPaddedWidth+x*bytesPerPixel+3];
					}
				}
			}
			else
			{
				// switch the image data from RGB to BGR
				for(y=(signed)height-1;y>=0;y--)
				{
					for(x=0;x<(signed)(width);x++)
					{
						tempData[y*destPaddedWidth+x*bytesPerPixel]=data[y*scrPaddedWidth+x*bytesPerPixel+2];
						tempData[y*destPaddedWidth+x*bytesPerPixel+1]=data[y*scrPaddedWidth+x*bytesPerPixel+1];
						tempData[y*destPaddedWidth+x*bytesPerPixel+2]=data[y*scrPaddedWidth+x*bytesPerPixel];
						tempData[y*destPaddedWidth+x*bytesPerPixel+3]=data[y*scrPaddedWidth+x*bytesPerPixel+3];
					}
				}
			}
			break;
	}

	// write the bitmap file header
	//out.write((char *)&bitmapFileHeader, sizeof(BITMAPFILEHEADER));
	out.write((char *)&bitmapFileHeader.bfType, sizeof(unsigned short));
	out.write((char *)&bitmapFileHeader.bfSize, sizeof(unsigned));
	out.write((char *)&bitmapFileHeader.bfReserved1, sizeof(unsigned short));
	out.write((char *)&bitmapFileHeader.bfReserved2, sizeof(unsigned short));
	out.write((char *)&bitmapFileHeader.bfOffBits, sizeof(unsigned));
	
	// write the bitmap info header
	//out.write((char *)&bitmapInfoHeader, sizeof(BITMAPINFOHEADER));
	out.write((char *)&bitmapInfoHeader.biSize, sizeof(unsigned));
	out.write((char *)&bitmapInfoHeader.biWidth, sizeof(int));
	out.write((char *)&bitmapInfoHeader.biHeight, sizeof(int));
	out.write((char *)&bitmapInfoHeader.biPlanes, sizeof(unsigned short));
	out.write((char *)&bitmapInfoHeader.biBitCount, sizeof(unsigned short));
	out.write((char *)&bitmapInfoHeader.biCompression, sizeof(unsigned));
	out.write((char *)&bitmapInfoHeader.biSizeImage, sizeof(unsigned));
	out.write((char *)&bitmapInfoHeader.biXPelsPerMeter, sizeof(int));
	out.write((char *)&bitmapInfoHeader.biYPelsPerMeter, sizeof(int));
	out.write((char *)&bitmapInfoHeader.biClrUsed, sizeof(unsigned int));
	out.write((char *)&bitmapInfoHeader.biClrImportant, sizeof(unsigned int));

	// write the image data
	out.write((char *)tempData, bitmapInfoHeader.biSizeImage);

	// close our file
	out.close();

	//frees memory
	delete[] tempData;

	return false;
}


//standard conversion from rgbe to float pixels 
//note: Ward uses ldexp(col+0.5,exp-(128+8)).  However we wanted pixels 
//in the range [0,1] to map back into the range [0,1].
//I think i got this function from NVIDIA SDK
void picture::rgbe2float(float &red, float &green, float &blue, unsigned char rgbe[4])
{
	if (rgbe[3]) //nonzero pixel
	{   
		float f = (float)ldexp(1.0,rgbe[3]-(int)(128+8));
		red = rgbe[0] * f;
		green = rgbe[1] * f;
		blue = rgbe[2] * f;
	}
	else
		red = green = blue = 0.0;
}
//simple read routine.  will not correctly handle run length encoding
//I think i got this function from NVIDIA SDK
int picture::RGBE_ReadPixels(ifstream &in, float *data, int numpixels)
{
	unsigned char rgbe[4];

	while(numpixels-- > 0) 
	{
		in.read((char *)rgbe,sizeof(rgbe));
		if(in.gcount()<1)
			return -1;	//cannot read data
		rgbe2float(data[0],data[1],data[2],rgbe);
		data += 3;
	}
	return 0;
}
//reads and uncompresses the file
//I think i got this function from NVIDIA SDK
int picture::RGBE_ReadPixels_RLE(ifstream &in, float *data, int width,int height)
{
	unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
	int i, count;
	unsigned char buf[2];

	if ((width < 8)||(width > 0x7fff))
		//run length encoding is not allowed so read flat
		return RGBE_ReadPixels(in,data,width*height);

	scanline_buffer = new unsigned char[sizeof(unsigned char)*4*width];
	if(!scanline_buffer) 
		return -1;	//unable to allocate buffer space

	//read in each successive scanline
	while(height > 0) 
	{
		in.read((char *)rgbe,sizeof(rgbe));
		if(in.gcount()<1)
		{
			delete[] scanline_buffer;
			return -1;	//cannot read data
		}
		if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) 
		{
			//this file is not run length encoded
			rgbe2float(data[0],data[1],data[2],rgbe);
			data += 3;
			delete[] scanline_buffer;
			return RGBE_ReadPixels(in,data,width*height-1);
		}
		if ((((int)rgbe[2])<<8 | rgbe[3]) != width) 
		{
			delete[] scanline_buffer;
			return -1;	//wrong scanline width
		}
		ptr = &scanline_buffer[0];
		//read each of the four channels for the scanline into the buffer
		for(i=0;i<4;i++) 
		{
			ptr_end = &scanline_buffer[(i+1)*width];
			while(ptr < ptr_end) 
			{
				in.read((char *)buf,sizeof(buf[0])*2);
				if(in.gcount()<1)
				{
					delete[] scanline_buffer;
					return -1;	//cannot read data
				}
				if (buf[0] > 128) 
				{
					//a run of the same value
					count = buf[0]-128;
					if ((count == 0)||(count > ptr_end - ptr)) 
					{
						delete[] scanline_buffer;
						return -1;	//bad scanline data
					}
					while(count-- > 0)
						*ptr++ = buf[1];
				}
				else 
				{
					// a non-run 
					count = buf[0];
					if ((count == 0)||(count > ptr_end - ptr)) 
					{
						delete[] scanline_buffer;
						return -1;	//bad scanline data
					}
					*ptr++ = buf[1];
					if (--count > 0)
					{
						in.read((char *)ptr,sizeof(*ptr)*count);
						if(in.gcount()<1)			
						{
							delete[] scanline_buffer;
							return -1;	//cannot read data
						}
						ptr += count;
					}
				}
			}
		}
		/* now convert data from buffer into floats */
		for(i=0;i<width;i++) 
		{
			rgbe[0] = scanline_buffer[i];
			rgbe[1] = scanline_buffer[i+width];
			rgbe[2] = scanline_buffer[i+2*width];
			rgbe[3] = scanline_buffer[i+3*width];
			rgbe2float(data[0],data[1],data[2],rgbe);
			data += 3;
		}
		height--;
	}
	delete[] scanline_buffer;
	return 0;
}

//load the HDR form a file
bool picture::readHDR(const char *fileName)
{
	char buf[128];
	ifstream in(fileName,ios::binary);
	if (!in)
		return false;

	in.getline(buf,128);
	if(!in.good())
	{
		in.close();
		return false;
	}

	//check for the token
	if(strcmp(buf,"#?RADIANCE"))
	{
		in.close();
		return false;
	}

	gamma=1.0f;
	exposure=1.0f;

	while(!in.eof())
	{
		in.getline(buf,128);
		if(!in.good())
			return false;
		
		if(strncmp(buf,"FORMAT",strlen("FORMAT"))==0)
		{

		}
		else if(strncmp(buf,"EXPOSURE",strlen("EXPOSURE"))==0)
		{
			sscanf(buf,"EXPOSURE=%f",&exposure);
		}
		else if(strncmp(buf,"GAMMA",strlen("GAMMA"))==0)
		{
			sscanf(buf,"GAMMA=%f",&gamma);
		}
		else if(strncmp(buf,"-Y",strlen("-Y"))==0)
		{
			sscanf(buf,"-Y %d +X %d",&height,&width);
			break;
		}
	}


	bytesPerPixel=3*sizeof(float);	//24 bit picture
	internalFormat=IMAGE_FORMAT_RGB_FLOAT_32;
	format=IMAGE_FORMAT_RGB;
	
	// allocate enough memory for the bitmap image data
	data = new unsigned char[width*height*bytesPerPixel];

	// verify memory allocation
	if (!data)
	{
		in.close();
		return false;
	}

	// read in the bitmap image data
	//in.read((char *)data,width*height*bytesPerPixel);

	int retVal=RGBE_ReadPixels_RLE(in,(float *)data,width,height);
	if(retVal==-1)
	{
		in.close();
		return false;
	}

	{
		unsigned char *surf=data;
		int depth=0;
		int _elementSize=bytesPerPixel;

		unsigned int lineSize;
		depth = (depth) ? depth : 1;
		lineSize = _elementSize * width;
		unsigned int sliceSize = lineSize * height;

		unsigned char *tempBuf = new unsigned char[lineSize];

		for ( int ii = 0; ii < depth; ii++) {
			unsigned char *top = surf + ii*sliceSize;
			unsigned char *bottom = top + (sliceSize - lineSize);

			for ( unsigned int jj = 0; jj < (height >> 1); jj++) {
				memcpy( tempBuf, top, lineSize);
				memcpy( top, bottom, lineSize);
				memcpy( bottom, tempBuf, lineSize);

				top += lineSize;
				bottom -= lineSize;
			}
		}

		delete []tempBuf;

	}

	// make sure bitmap image data was read
	if(in.bad())
	{
		in.close();
		return false;
	}
	
	// close the file and return the bitmap image data
	in.close();
	return true;
}
//converts depth map to visible map
picture *picture::depthToVisible(const unsigned int &width,const unsigned int &height,const double &nearPlane, const double &farPlane, float *data)
{
	picture *output=new picture;
	if(!output)
		return NULL;

	output->width=width;
	output->height=height;
	output->bytesPerPixel=3;
	output->format=IMAGE_FORMAT_BGR;
	output->gamma=1.0f;
	output->exposure=1.0f;
	output->data=new unsigned char[output->width*output->height*output->bytesPerPixel];
		
	// verify memory allocation
	if (!output->data)
	{
		delete output;
		return NULL;
	}

	int x,y,temp;

	//copy data
	for(y=(signed)height-1;y>=0;--y)
	{
		for(x=(signed)width-1;x>=0;--x)
		{
			temp=(y*output->width+x)*output->bytesPerPixel;
			output->data[temp+2]=output->data[temp+1]=output->data[temp]=(unsigned char)(255*(2.0*nearPlane)/(farPlane+nearPlane-(data[y*width+x]*(farPlane-nearPlane))));
		}
	}
	
	return output;
}

//converts visible map to depth map
picture *picture::visibleToDepth(void)
{
	picture *output=new picture;
	if(!output)
		return NULL;

	output->width=width;
	output->height=height;
	output->bytesPerPixel=1;
	output->format=IMAGE_FORMAT_DEPTH_32;
	output->gamma=1.0f;
	output->exposure=1.0f;
	output->data=new unsigned char[output->width*output->height*output->bytesPerPixel];
		
	// verify memory allocation
	if (!output->data)
	{
		delete output;
		return NULL;
	}

	int x,y;

	//copy data
	for(y=(signed)height-1;y>=0;--y)
	{
		for(x=(signed)width-1;x>=0;--x)
		{
			output->data[y*output->width+x]=data[(y*width+x)*bytesPerPixel];
		}
	}
	
	return output;
}

//frees memory
void picture::clear(void)
{
	delete[] data;
	data=NULL;
}
//destructor
picture::~picture()
{
	//frees memory
	clear();			
}